package com.haiyou.common.spring.util;

import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import com.google.common.collect.Lists;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

import com.google.common.collect.Maps;

/**
 * 
 * 
 * @Description: spring 工具类
 * @author xingyuan
 * @date 2022年5月17日 下午8:29:10
 */
@Component
public class SpringUtil implements ApplicationContextAware{

	public static ApplicationContext applicationContext;

	/**
	 * 此方法有一定漏洞 未进行 bean 的修改和 销毁缓存监听
	 */
	private static Map<Class<?>, Object> classBeanCache = Maps.newConcurrentMap();
	
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		SpringUtil.applicationContext = applicationContext;
	}

	@SuppressWarnings("unchecked")
	public static <T> T getBean(Class<T> requiredType) {
		//注意此处这么写,大部分情况下不需要创建lambda表达式
	    Object bean = classBeanCache.getOrDefault(requiredType, null);
		
	    if(bean == null) {
	    	return (T) classBeanCache.computeIfAbsent(requiredType, k->{
				try {
					return applicationContext.getBean(requiredType);
				} catch (Exception e) {
					return null;
				}
			});
	    }
	    
		return (T)bean;
	}

	public static <T> List<T> getBeans(Class<T> clazz){
		Map<String,T> map = applicationContext.getBeansOfType(clazz);
		return Objects.isNull(map) ? Lists.newArrayList() : Lists.newArrayList(map.values());
	}
	
	public static <T> T getBean(String bean,Class<T> requiredType) {
		return applicationContext.getBean(bean, requiredType);
	}
	

	public static <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) {
		return applicationContext.getBeansOfType(type);
	}

	public static Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) {
		return applicationContext.getBeansWithAnnotation(annotationType);
	}

	
	

    /**
    * 主动向Spring容器中注册bean
    * 注意,次方法不会注册@Bean 标记的方法级bean
    * @param applicationContext Spring容器
    * @param name               BeanName
    * @param clazz              注册的bean的类性
    * @param args               构造方法的必要参数，顺序和类型要求和clazz中定义的一致
    * @param <T>
    * @return 返回注册到容器中的bean对象
    */
   public static <T> T registerBean(String name, Class<T> clazz) {
	   
       if(applicationContext.containsBean(name)) {
           Object bean = applicationContext.getBean(name);
           if (bean.getClass().isAssignableFrom(clazz)) {
               return (T) bean;
           } else {
               throw new RuntimeException("BeanName 重复 " + name);
           }
       }

       BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry) ((ConfigurableApplicationContext)applicationContext).getBeanFactory();
       
       beanFactory.registerBeanDefinition(name,new AnnotatedGenericBeanDefinition(clazz));
       
       return applicationContext.getBean(name, clazz);
   }
	
   
   
	
}
